home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / threshold.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-29  |  13.7 KB  |  583 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <glib.h>
  22.  
  23. #include "apptypes.h"
  24.  
  25. #include "appenv.h"
  26. #include "drawable.h"
  27. #include "gdisplay.h"
  28. #include "gimpui.h"
  29. #include "threshold.h"
  30.  
  31. #include "libgimp/gimpintl.h"
  32.  
  33.  
  34. #define HISTOGRAM_WIDTH  256
  35. #define HISTOGRAM_HEIGHT 150
  36.  
  37. #define LOW        0x1
  38. #define HIGH       0x2
  39. #define HISTORGAM  0x4
  40. #define ALL       (LOW | HIGH | HISTOGRAM)
  41.  
  42. /*  the threshold structures  */
  43.  
  44. typedef struct _Threshold Threshold;
  45.  
  46. struct _Threshold
  47. {
  48.   gint x, y;    /*  coords for last mouse click  */
  49. };
  50.  
  51. /*  the threshold tool options  */
  52. static ToolOptions *threshold_options = NULL;
  53.  
  54. /*  the threshold tool dialog  */
  55. static ThresholdDialog *threshold_dialog = NULL;
  56.  
  57. /*  threshold action functions  */
  58. static void   threshold_control (Tool *, ToolAction, gpointer);
  59.  
  60. static ThresholdDialog * threshold_dialog_new (void);
  61.  
  62. static void   threshold_update                     (ThresholdDialog *,
  63.                             gint);
  64. static void   threshold_preview                    (ThresholdDialog *);
  65. static void   threshold_reset_callback             (GtkWidget *, gpointer);
  66. static void   threshold_ok_callback                (GtkWidget *, gpointer);
  67. static void   threshold_cancel_callback            (GtkWidget *, gpointer);
  68. static void   threshold_preview_update             (GtkWidget *, gpointer);
  69. static void   threshold_low_threshold_adjustment_update  (GtkAdjustment *,
  70.                               gpointer);
  71. static void   threshold_high_threshold_adjustment_update (GtkAdjustment *,
  72.                               gpointer);
  73.  
  74. static void   threshold                 (PixelRegion *, PixelRegion *, void *);
  75. static void   threshold_histogram_range (HistogramWidget *, gint, gint,
  76.                      gpointer);
  77. /*  threshold machinery  */
  78.  
  79. void
  80. threshold_2 (void        *data,
  81.          PixelRegion *srcPR,
  82.          PixelRegion *destPR)
  83. {
  84.   /*  this function just re-orders the arguments so we can use 
  85.    *  pixel_regions_process_paralell
  86.    */
  87.   threshold (srcPR, destPR, data);
  88. }
  89.  
  90. static void
  91. threshold (PixelRegion *srcPR,
  92.        PixelRegion *destPR,
  93.        void        *data)
  94. {
  95.   ThresholdDialog *td;
  96.   unsigned char *src, *s;
  97.   unsigned char *dest, *d;
  98.   int has_alpha, alpha;
  99.   int w, h, b;
  100.   int value;
  101.  
  102.   td = (ThresholdDialog *) data;
  103.  
  104.   h = srcPR->h;
  105.   src = srcPR->data;
  106.   dest = destPR->data;
  107.   has_alpha = (srcPR->bytes == 2 || srcPR->bytes == 4);
  108.   alpha = has_alpha ? srcPR->bytes - 1 : srcPR->bytes;
  109.  
  110.   while (h--)
  111.     {
  112.       w = srcPR->w;
  113.       s = src;
  114.       d = dest;
  115.       while (w--)
  116.     {
  117.       if (td->color)
  118.         {
  119.           value = MAX (s[RED_PIX], s[GREEN_PIX]);
  120.           value = MAX (value, s[BLUE_PIX]);
  121.  
  122.           value = (value >= td->low_threshold && value <= td->high_threshold ) ? 255 : 0;
  123.         }
  124.       else
  125.         value = (s[GRAY_PIX] >= td->low_threshold && s[GRAY_PIX] <= td->high_threshold) ? 255 : 0;
  126.  
  127.       for (b = 0; b < alpha; b++)
  128.         d[b] = value;
  129.  
  130.       if (has_alpha)
  131.         d[alpha] = s[alpha];
  132.  
  133.       s += srcPR->bytes;
  134.       d += destPR->bytes;
  135.     }
  136.  
  137.       src += srcPR->rowstride;
  138.       dest += destPR->rowstride;
  139.     }
  140. }
  141.  
  142. /*  threshold action functions  */
  143.  
  144. static void
  145. threshold_control (Tool       *tool,
  146.            ToolAction  action,
  147.            gpointer    gdisp_ptr)
  148. {
  149.   switch (action)
  150.     {
  151.     case PAUSE:
  152.       break;
  153.  
  154.     case RESUME:
  155.       break;
  156.  
  157.     case HALT:
  158.       threshold_dialog_hide ();
  159.       break;
  160.  
  161.     default:
  162.       break;
  163.     }
  164. }
  165.  
  166. Tool *
  167. tools_new_threshold (void)
  168. {
  169.   Tool * tool;
  170.   Threshold * private;
  171.  
  172.   /*  The tool options  */
  173.   if (! threshold_options)
  174.     {
  175.       threshold_options = tool_options_new (_("Threshold"));
  176.       tools_register (THRESHOLD, threshold_options);
  177.     }
  178.  
  179.   tool = tools_new_tool (THRESHOLD);
  180.   private = g_new0 (Threshold, 1);
  181.  
  182.   tool->scroll_lock = TRUE;   /*  Disallow scrolling  */
  183.   tool->preserve    = FALSE;  /*  Don't preserve on drawable change  */
  184.  
  185.   tool->private = (void *) private;
  186.  
  187.   tool->control_func = threshold_control;
  188.  
  189.   return tool;
  190. }
  191.  
  192. void
  193. threshold_dialog_hide (void)
  194. {
  195.   if (threshold_dialog)
  196.     threshold_cancel_callback (NULL, (gpointer) threshold_dialog);
  197. }
  198.   
  199. void
  200. tools_free_threshold (Tool *tool)
  201. {
  202.   Threshold * thresh;
  203.  
  204.   thresh = (Threshold *) tool->private;
  205.  
  206.   /*  Close the threshold dialog  */
  207.   threshold_dialog_hide ();
  208.  
  209.   g_free (thresh);
  210. }
  211.  
  212. void
  213. threshold_initialize (GDisplay *gdisp)
  214. {
  215.   if (gimp_image_is_empty (gdisp->gimage))
  216.     {
  217.       g_message (_("The image has no drawables."));
  218.       return;
  219.     }
  220.  
  221.   if (drawable_indexed (gimage_active_drawable (gdisp->gimage)))
  222.     {
  223.       g_message (_("Threshold does not operate on indexed drawables."));
  224.       return;
  225.     }
  226.  
  227.   /*  The threshold dialog  */
  228.   if (!threshold_dialog)
  229.     threshold_dialog = threshold_dialog_new ();
  230.   else
  231.     if (!GTK_WIDGET_VISIBLE (threshold_dialog->shell))
  232.       gtk_widget_show (threshold_dialog->shell);
  233.  
  234.   threshold_dialog->low_threshold  = 127;
  235.   threshold_dialog->high_threshold = 255;
  236.  
  237.   threshold_dialog->drawable = gimage_active_drawable (gdisp->gimage);
  238.   threshold_dialog->color = drawable_color (threshold_dialog->drawable);
  239.   threshold_dialog->image_map =
  240.     image_map_create (gdisp, threshold_dialog->drawable);
  241.  
  242.   gimp_histogram_calculate_drawable (threshold_dialog->hist,
  243.                      threshold_dialog->drawable);
  244.  
  245.   gtk_signal_handler_block_by_data (GTK_OBJECT (threshold_dialog->histogram),
  246.                     threshold_dialog);
  247.   histogram_widget_update (threshold_dialog->histogram,
  248.                threshold_dialog->hist);
  249.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (threshold_dialog->histogram),
  250.                       threshold_dialog);
  251.  
  252.   threshold_update (threshold_dialog, ALL);
  253.  
  254.   if (threshold_dialog->preview)
  255.     threshold_preview (threshold_dialog);
  256.  
  257.   /* Merge-related undo release signal */
  258.  
  259.    threshold_dialog->conn_id =
  260.      gtk_signal_connect (
  261.              GTK_OBJECT (gdisp->gimage), "layer_merge",
  262.              GTK_SIGNAL_FUNC (threshold_cancel_callback),
  263.              threshold_dialog
  264.             );
  265.  
  266. }
  267.  
  268. /**********************/
  269. /*  Threshold dialog  */
  270. /**********************/
  271.  
  272. static ThresholdDialog *
  273. threshold_dialog_new (void)
  274. {
  275.   ThresholdDialog *td;
  276.   GtkWidget *vbox;
  277.   GtkWidget *hbox;
  278.   GtkWidget *spinbutton;
  279.   GtkWidget *label;
  280.   GtkWidget *frame;
  281.   GtkWidget *toggle;
  282.   GtkObject *data;
  283.  
  284.   td = g_new (ThresholdDialog, 1);
  285.   td->preview        = TRUE;
  286.   td->low_threshold  = 127;
  287.   td->high_threshold = 255;
  288.   td->hist           = gimp_histogram_new ();
  289.  
  290.   /*  The shell and main vbox  */
  291.   td->shell =
  292.     gimp_dialog_new (_("Threshold"), "threshold",
  293.              tools_help_func, NULL,
  294.              GTK_WIN_POS_NONE,
  295.              FALSE, TRUE, FALSE,
  296.  
  297.              _("OK"), threshold_ok_callback,
  298.              td, NULL, NULL, TRUE, FALSE,
  299.              _("Reset"), threshold_reset_callback,
  300.              td, NULL, NULL, TRUE, FALSE,
  301.              _("Cancel"), threshold_cancel_callback,
  302.              td, NULL, NULL, FALSE, TRUE,
  303.  
  304.              NULL);
  305.  
  306.   vbox = gtk_vbox_new (FALSE, 4);
  307.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  308.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (td->shell)->vbox), vbox);
  309.  
  310.   /*  Horizontal box for threshold text widget  */
  311.   hbox = gtk_hbox_new (FALSE, 4);
  312.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  313.  
  314.   label = gtk_label_new (_("Threshold Range:"));
  315.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  316.   gtk_widget_show (label);
  317.  
  318.   /*  low threshold spinbutton  */
  319.   data = gtk_adjustment_new (td->low_threshold, 0.0, 255.0, 1.0, 10.0, 0.0);
  320.   td->low_threshold_data = GTK_ADJUSTMENT (data);
  321.  
  322.   spinbutton = gtk_spin_button_new (td->low_threshold_data, 1.0, 0);
  323.   gtk_widget_set_usize (spinbutton, 75, -1);
  324.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  325.  
  326.   gtk_signal_connect (GTK_OBJECT (td->low_threshold_data), "value_changed",
  327.               GTK_SIGNAL_FUNC (threshold_low_threshold_adjustment_update),
  328.               td);
  329.  
  330.   gtk_widget_show (spinbutton);
  331.  
  332.   /* high threshold spinbutton  */
  333.   data = gtk_adjustment_new (td->high_threshold, 0.0, 255.0, 1.0, 10.0, 0.0);
  334.   td->high_threshold_data = GTK_ADJUSTMENT (data);
  335.  
  336.   spinbutton = gtk_spin_button_new (td->high_threshold_data, 1.0, 0);
  337.   gtk_widget_set_usize (spinbutton, 75, -1);
  338.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  339.  
  340.   gtk_signal_connect (GTK_OBJECT (td->high_threshold_data), "value_changed",
  341.               GTK_SIGNAL_FUNC (threshold_high_threshold_adjustment_update),
  342.               td);
  343.  
  344.   gtk_widget_show (spinbutton);
  345.  
  346.   gtk_widget_show (hbox);
  347.  
  348.   /*  The threshold histogram  */
  349.   hbox = gtk_hbox_new (TRUE, 0);
  350.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  351.  
  352.   frame = gtk_frame_new (NULL);
  353.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  354.   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, FALSE, 0);
  355.  
  356.   td->histogram = histogram_widget_new (HISTOGRAM_WIDTH, HISTOGRAM_HEIGHT);
  357.  
  358.   gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (td->histogram));
  359.  
  360.   gtk_signal_connect (GTK_OBJECT (td->histogram), "range_changed",
  361.               GTK_SIGNAL_FUNC (threshold_histogram_range),
  362.               td);
  363.  
  364.   gtk_widget_show (GTK_WIDGET(td->histogram));
  365.  
  366.   gtk_widget_show (frame);
  367.   gtk_widget_show (hbox);
  368.  
  369.   /*  Horizontal box for preview  */
  370.   hbox = gtk_hbox_new (FALSE, 4);
  371.   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  372.  
  373.   /*  The preview toggle  */
  374.   toggle = gtk_check_button_new_with_label (_("Preview"));
  375.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), td->preview);
  376.   gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
  377.  
  378.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  379.               GTK_SIGNAL_FUNC (threshold_preview_update),
  380.               td);
  381.  
  382.   gtk_widget_show (toggle);
  383.   gtk_widget_show (hbox);
  384.  
  385.   gtk_widget_show (vbox);
  386.   gtk_widget_show (td->shell);
  387.  
  388.   return td;
  389. }
  390.  
  391. static void
  392. threshold_update (ThresholdDialog *td,
  393.           gint             update)
  394. {
  395.   if (update & LOW)
  396.     {
  397.       gtk_adjustment_set_value (td->low_threshold_data, td->low_threshold);
  398.     }
  399.   if (update & HIGH)
  400.     {
  401.       gtk_adjustment_set_value (td->high_threshold_data, td->high_threshold);
  402.     }
  403.   if (update & HISTOGRAM)
  404.     {
  405.       histogram_widget_range (td->histogram,
  406.                   td->low_threshold,
  407.                   td->high_threshold);
  408.     }
  409. }
  410.  
  411. static void
  412. threshold_preview (ThresholdDialog *td)
  413. {
  414.   if (!td->image_map)
  415.     {
  416.       g_warning ("threshold_preview(): No image map");
  417.       return;
  418.     }
  419.  
  420.   active_tool->preserve = TRUE;
  421.   image_map_apply (td->image_map, threshold, td);
  422.   active_tool->preserve = FALSE;
  423. }
  424.  
  425. static void
  426. threshold_reset_callback (GtkWidget *widget,
  427.               gpointer   data)
  428. {
  429.   ThresholdDialog *td;
  430.  
  431.   td = (ThresholdDialog *) data;
  432.  
  433.   td->low_threshold  = 127.0;
  434.   td->high_threshold = 255.0;
  435.  
  436.   threshold_update (td, ALL);
  437.  
  438.   if (td->preview)
  439.     threshold_preview (td);
  440. }
  441.  
  442. static void
  443. threshold_ok_callback (GtkWidget *widget,
  444.                gpointer   data)
  445. {
  446.   ThresholdDialog *td;
  447.  
  448.   td = (ThresholdDialog *) data;
  449.  
  450.   gimp_dialog_hide (td->shell);
  451.   
  452.   active_tool->preserve = TRUE;
  453.  
  454.   if (!td->preview)
  455.     image_map_apply (td->image_map, threshold, (void *) td);
  456.  
  457.   if (td->image_map)
  458.     image_map_commit (td->image_map);
  459.  
  460.   active_tool->preserve = FALSE;
  461.  
  462.   td->image_map = NULL;
  463.  
  464.   active_tool->gdisp_ptr = NULL;
  465.   active_tool->drawable = NULL;
  466. }
  467.  
  468. static void
  469. threshold_cancel_callback (GtkWidget *widget,
  470.                gpointer   data)
  471. {
  472.   ThresholdDialog *td;
  473.  
  474.   td = (ThresholdDialog *) data;
  475.  
  476.   gimp_dialog_hide (td->shell);
  477.  
  478.   if (td->image_map)
  479.     {
  480.       active_tool->preserve = TRUE;
  481.       image_map_abort (td->image_map);
  482.       active_tool->preserve = FALSE;
  483.  
  484.       td->image_map = NULL;
  485.       gdisplays_flush ();
  486.     }
  487.  
  488.   if (td->conn_id != 0)
  489.     {
  490.       gtk_signal_disconnect (
  491.                  GTK_OBJECT (gimp_drawable_gimage (td->drawable)), 
  492.                  td->conn_id
  493.                 );
  494.       td->conn_id = 0;
  495.     }
  496.  
  497.   active_tool->gdisp_ptr = NULL;
  498.   active_tool->drawable = NULL;
  499. }
  500.  
  501. static void
  502. threshold_preview_update (GtkWidget *widget,
  503.               gpointer   data)
  504. {
  505.   ThresholdDialog *td;
  506.  
  507.   td = (ThresholdDialog *) data;
  508.  
  509.   if (GTK_TOGGLE_BUTTON (widget)->active)
  510.     {
  511.       td->preview = TRUE;
  512.       threshold_preview (td);
  513.     }
  514.   else
  515.     {    
  516.       td->preview = FALSE;
  517.       if (td->image_map)
  518.     {
  519.       active_tool->preserve = TRUE;
  520.       image_map_clear (td->image_map);
  521.       active_tool->preserve = FALSE;
  522.       gdisplays_flush ();
  523.     }
  524.     }
  525. }
  526.  
  527. static void
  528. threshold_low_threshold_adjustment_update (GtkAdjustment *adjustment,
  529.                        gpointer       data)
  530. {
  531.   ThresholdDialog *td;
  532.  
  533.   td = (ThresholdDialog *) data;
  534.  
  535.   if (td->low_threshold != adjustment->value)
  536.     {
  537.       td->low_threshold = adjustment->value;
  538.  
  539.       threshold_update (td, HISTOGRAM);
  540.  
  541.       if (td->preview)
  542.     threshold_preview (td);
  543.     }
  544. }
  545.  
  546. static void
  547. threshold_high_threshold_adjustment_update (GtkAdjustment *adjustment,
  548.                         gpointer       data)
  549. {
  550.   ThresholdDialog *td;
  551.  
  552.   td = (ThresholdDialog *) data;
  553.  
  554.   if (td->high_threshold != adjustment->value)
  555.     {
  556.       td->high_threshold = adjustment->value;
  557.  
  558.       threshold_update (td, HISTOGRAM);
  559.  
  560.       if (td->preview)
  561.     threshold_preview (td);
  562.     }
  563. }
  564.  
  565. static void
  566. threshold_histogram_range (HistogramWidget *widget,
  567.                gint             start,
  568.                gint             end,
  569.                gpointer         data)
  570. {
  571.   ThresholdDialog *td;
  572.  
  573.   td = (ThresholdDialog *) data;
  574.  
  575.   td->low_threshold  = start;
  576.   td->high_threshold = end;
  577.  
  578.   threshold_update (td, LOW | HIGH);
  579.  
  580.   if (td->preview)
  581.     threshold_preview (td);
  582. }
  583.